var tgd = tgd || {};
tgd.Tools = tgd.Tools || {};
tgd.Tools.ContactDiff = tgd.Tools.ContactDiff || {};

/*
 * Constructs a unique contact.
 *
 * name - String - The unique "name" for the contact.  This is the "title" property returned from Google.
 * allUsers - function - function pointer to get all of the users, not just those that have this contact
 */
tgd.Tools.ContactDiff.Contact = function (name, allUsers) {
	var _users = [];
	var _name = name;
	var _props = {};
	var _isIdentical = true;
	var _numMatch;
	var _numDiff;
	var _propStuff;
	var _allUsers = allUsers;

	this.__defineGetter__("Name", function() { return _name; });
	this.__defineGetter__("Properties", function() { return _props; });
	this.__defineGetter__("IsUnique", function() { return _users.length === 1; });
	this.__defineGetter__("Users", function() { return _users; });
	this.__defineGetter__("AllUserCount", function() { return _allUsers().length; });  //get the count of all users, not just those that have this contact
	this.__defineGetter__("AllUsers", function() { return _allUsers(); });  //get all users, not just those that have this contact
	this.__defineGetter__("IsIdentical", function() { return _isIdentical && _users.length === this.AllUserCount; });

    /*
     * Determine the number of properties that have differences and matches.
     *
     * obj - tgd.Tools.ContactDiff.Property | Object - The properties object to check for diffs/matches.
     * propCounter - { NumMatches : Number, NumDiffs : Number } - Counter for diff/matches of properties.
     */
    this._countProperties = function(obj, propCounter) {
        for (var prop in obj) {
            if (obj[prop] instanceof tgd.Tools.ContactDiff.Property) {
                var pp = obj[prop];
                if (pp.AllowMultiples) {
                    for (var i = 0; i < pp.Values.length; ++i) {
                        if (pp.Values[i].Users.length === this.AllUserCount) {
                            ++propCounter.NumMatches;
                        } else {
                            ++propCounter.NumDiffs;
                        }
                    }
                } else {
                    if (!pp.HasDiffs && pp.UserCount == this.AllUserCount) {
                        ++propCounter.NumMatches;
                    } else {
                        ++propCounter.NumDiffs;
                    }
                }
            } else if (typeof obj[prop] === "string" || typeof obj[prop] === "number") {
			    console.error("This should never happen.  ", prop, obj[prop]);
		    } else if (obj[prop] instanceof Array) {
			    console.error("This should never happen.  ", prop, obj[prop]);
		    } else { //object
			    this._countProperties(obj[prop], propCounter);
		    }
        }

        return propCounter;
    }

	this.__defineGetter__("NumPropertyDifferences", function() { return (_propStuff === undefined ? (_propStuff = this._countProperties(_props, { NumMatches : 0, NumDiffs : 0 })).NumDiffs : _propStuff.NumDiffs); });
	this.__defineGetter__("NumPropertyMatches", function() { return (_propStuff === undefined ? (_propStuff = this._countProperties(_props, { NumMatches : 0, NumDiffs : 0 })).NumMatches : _propStuff.NumMatches); });

	if (tgd.Tools.ContactDiff.Contact._initialized === undefined) {

	    /*
	     * Enumeration to cleanup the property names.
	     */
		tgd.Tools.ContactDiff.Contact.prototype._cleanPropertyNames = [
			{ Old : "title.$t", New : "Title" },
			{ Old : "gd$name.gd$givenName.$t", New : "Given Name" },
			{ Old : "gd$name.gd$familyName.$t", New : "Family Name" },
			{ Old : "gContact$birthday.when", New : "Birthday" },
			{ Old : "gd$name.gd$fullName.$t", New : "Full Name" },
			{ Old : "gd$organization", New : "Organization" },
			{ Old : "gd$email", New : "Email" },
			{ Old : "gd$im", New : "IM" },
			{ Old : "gd$phoneNumber", New : "Phone #" },
			{ Old : "gd$structuredPostalAddress", New : "Address" },
			{ Old : "gContact$event", New : "Event" },
			{ Old : "gContact$relation", New : "Relation" },
			{ Old : "gContact$website", New : "Website" },
			{ Old : "gContact$groupMembershipInfo", New : "Group" },
			{ Old : "gContact$nickname.$t", New : "Nickname" },
			{ Old : "gd$name.gd$namePrefix.$t", New : "Name Prefix" },
			{ Old : "gd$name.gd$additionalName.$t", New : "Additional Name" },
		];

        /*
         * Takes the given property string [p], and returns a "friendlier" one (if exists).
         *
         * p - String - The name of a property from the Google returned object.
         *
         * return String
         *      Cleaned property name.
         */
		tgd.Tools.ContactDiff.Contact.prototype._cleanPropertyName = function(p) {
			for (var i = 0; i < this._cleanPropertyNames.length; ++i) {
				if (this._cleanPropertyNames[i].Old === p) {
					return this._cleanPropertyNames[i].New;
				}
			}
			return p;
		}

		tgd.Tools.ContactDiff.Contact._initialized = true;
	}

	/*
	 * TODO: cleanup and names and document
	 * u - tgd.Tools.ContactDiff.User object associated with current data
	 * d - data associated with current user
	 * orig - master data associated with this contact
	 * path - current property path of data
	 */
	this._foo = function(u, d, orig, path) {
		for (var p in d) {
			var currentPath = path + (path === "" ? "" : ".") + p;

			if (orig[p] === undefined) {
				if (_users.length !== 0) {  //if this is the first user, then still identical
					_isIdentical = false;
				}

				if (typeof d[p] === "string" || typeof d[p] === "number") {
					orig[p] = new tgd.Tools.ContactDiff.Property(this._cleanPropertyName(currentPath), false);
					orig[p].add(d[p], u);
				} else if (d[p] instanceof Array) {
					orig[p] = new tgd.Tools.ContactDiff.Property(this._cleanPropertyName(currentPath), true);
					for (var i = 0; i < d[p].length; ++i) {
					    orig[p].add(d[p][i], u);  //TODO: figure this out, parameters unknown
					}
				} else { //object
					orig[p] = {};
					this._foo(u, d[p], orig[p], currentPath);
				}
			} else {
				if (typeof d[p] === "string" || typeof d[p] === "number") {
					_isIdentical = (orig[p].add(d[p], u) ? _isIdentical : false);
				} else if (d[p] instanceof Array) {
					for (var i = 0; i < d[p].length; ++i) {
						_isIdentical = (orig[p].add(d[p][i], u) ? _isIdentical : false);
					}
				} else { //object
					this._foo(u, d[p], orig[p], currentPath);
				}
			}
		}

		for (var p in orig) {  //contact doesn't contain all properties
			if (d[p] === undefined) {
				_isIdentical = false;
			}
		}
	}

	/*
	 * Add the contact represented by [data] to [user].
	 *
	 * user - tgd.Tools.ContactDiff.User - User to add the given contact ([data]) to.
	 * data - Object - Data object stored for a given contact.
	 */
	this.add = function(user, data) {
		this._foo(user, data, _props, "");
		_users.push(user);
	}
}
